{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pybroker\n",
    "from pybroker import Strategy, StrategyConfig, YFinance\n",
    "from pybroker.ext.data import AKShare\n",
    "\n",
    "pybroker.enable_data_source_cache('my_strategy')\n",
    "\n",
    "#================== 交易策略    ====================================\n",
    "def buy_low(ctx):\n",
    "    if ctx.long_pos():\n",
    "        return\n",
    "    if ctx.bars >= 2 and ctx.close[-1] < ctx.low[-2]:\n",
    "        ctx.buy_shares = ctx.calc_target_shares(0.25)\n",
    "        ctx.buy_limit_price = ctx.close[-1] - 0.01\n",
    "        ctx.hold_bars = 3\n",
    "\n",
    "def short_high(ctx):\n",
    "    if ctx.short_pos():\n",
    "        return\n",
    "    if ctx.bars >= 2 and ctx.close[-1] > ctx.high[-2]:\n",
    "        ctx.sell_shares = 100\n",
    "        ctx.hold_bars = 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "#=============================== 创建一个新的策略实例=============================\n",
    "#bootstrap_sample_size用于计算的每个随机样本的大小 引导指标。默认值为 。1000\n",
    "config = StrategyConfig(initial_cash=500_000, bootstrap_sample_size=100)\n",
    "strategy = Strategy(AKShare(), '3/1/2020', '3/1/2024', config)\n",
    "strategy.add_execution(buy_low, ['000001', '000002'])\n",
    "strategy.add_execution(short_high, ['000003'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Backtesting: 2020-03-01 00:00:00 to 2024-03-01 00:00:00\n",
      "\n",
      "Loading bar data...\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loaded bar data: 0:00:01 \n",
      "\n",
      "Test split: 2020-03-02 00:00:00 to 2024-03-01 00:00:00\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\ProgramData\\Anaconda3\\envs\\pybroker\\Lib\\site-packages\\pybroker\\data.py:244: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.\n",
      "  df = pd.concat((cached_df, df))\n",
      "  0% (0 of 972) |                        | Elapsed Time: 0:00:00 ETA:  --:--:--\n",
      "  2% (21 of 972) |                       | Elapsed Time: 0:00:00 ETA:   0:00:05\n",
      "  4% (41 of 972) |                       | Elapsed Time: 0:00:00 ETA:   0:00:04\n",
      "  5% (51 of 972) |#                      | Elapsed Time: 0:00:00 ETA:   0:00:04\n",
      "  7% (71 of 972) |#                      | Elapsed Time: 0:00:00 ETA:   0:00:04\n",
      "  8% (81 of 972) |#                      | Elapsed Time: 0:00:00 ETA:   0:00:04\n",
      " 10% (101 of 972) |##                    | Elapsed Time: 0:00:00 ETA:   0:00:05\n",
      " 13% (131 of 972) |##                    | Elapsed Time: 0:00:00 ETA:   0:00:04\n",
      " 16% (161 of 972) |###                   | Elapsed Time: 0:00:00 ETA:   0:00:03\n",
      " 19% (191 of 972) |####                  | Elapsed Time: 0:00:00 ETA:   0:00:03\n",
      " 20% (201 of 972) |####                  | Elapsed Time: 0:00:00 ETA:   0:00:03\n",
      " 23% (231 of 972) |#####                 | Elapsed Time: 0:00:00 ETA:   0:00:02\n",
      " 26% (261 of 972) |#####                 | Elapsed Time: 0:00:00 ETA:   0:00:02\n",
      " 29% (291 of 972) |######                | Elapsed Time: 0:00:01 ETA:   0:00:02\n",
      " 31% (311 of 972) |#######               | Elapsed Time: 0:00:01 ETA:   0:00:02\n",
      " 35% (341 of 972) |#######               | Elapsed Time: 0:00:01 ETA:   0:00:02\n",
      " 38% (371 of 972) |########              | Elapsed Time: 0:00:01 ETA:   0:00:02\n",
      " 42% (411 of 972) |#########             | Elapsed Time: 0:00:01 ETA:   0:00:01\n",
      " 46% (451 of 972) |##########            | Elapsed Time: 0:00:01 ETA:   0:00:01\n",
      " 49% (481 of 972) |##########            | Elapsed Time: 0:00:01 ETA:   0:00:01\n",
      " 51% (501 of 972) |###########           | Elapsed Time: 0:00:01 ETA:   0:00:01\n",
      " 54% (531 of 972) |############          | Elapsed Time: 0:00:01 ETA:   0:00:01\n",
      " 58% (571 of 972) |############          | Elapsed Time: 0:00:01 ETA:   0:00:01\n",
      " 60% (591 of 972) |#############         | Elapsed Time: 0:00:01 ETA:   0:00:01\n",
      " 64% (631 of 972) |##############        | Elapsed Time: 0:00:01 ETA:   0:00:00\n",
      " 68% (661 of 972) |##############        | Elapsed Time: 0:00:01 ETA:   0:00:00\n",
      " 73% (711 of 972) |################      | Elapsed Time: 0:00:01 ETA:   0:00:00\n",
      " 76% (741 of 972) |################      | Elapsed Time: 0:00:01 ETA:   0:00:00\n",
      " 79% (771 of 972) |#################     | Elapsed Time: 0:00:02 ETA:   0:00:00\n",
      " 84% (821 of 972) |##################    | Elapsed Time: 0:00:02 ETA:   0:00:00\n",
      " 87% (851 of 972) |###################   | Elapsed Time: 0:00:02 ETA:   0:00:00\n",
      " 90% (881 of 972) |###################   | Elapsed Time: 0:00:02 ETA:   0:00:00\n",
      " 92% (901 of 972) |####################  | Elapsed Time: 0:00:02 ETA:   0:00:00\n",
      " 93% (911 of 972) |####################  | Elapsed Time: 0:00:02 ETA:   0:00:00\n",
      " 95% (931 of 972) |##################### | Elapsed Time: 0:00:02 ETA:   0:00:00\n",
      " 97% (951 of 972) |##################### | Elapsed Time: 0:00:02 ETA:   0:00:00\n",
      "100% (972 of 972) |######################| Elapsed Time: 0:00:02 Time:  0:00:02\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Calculating bootstrap metrics: sample_size=100, samples=10000...\n",
      "Calculated bootstrap metrics: 0:00:19 \n",
      "\n",
      "Finished backtest: 0:00:38\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>name</th>\n",
       "      <th>value</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>trade_count</td>\n",
       "      <td>1.900000e+02</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>initial_market_value</td>\n",
       "      <td>5.000000e+05</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>end_market_value</td>\n",
       "      <td>3.800547e+05</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>total_pnl</td>\n",
       "      <td>-1.199453e+05</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>unrealized_pnl</td>\n",
       "      <td>-1.455192e-11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>total_return_pct</td>\n",
       "      <td>-2.398906e+01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>total_profit</td>\n",
       "      <td>1.884148e+05</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>total_loss</td>\n",
       "      <td>-3.083601e+05</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>total_fees</td>\n",
       "      <td>0.000000e+00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>max_drawdown</td>\n",
       "      <td>-1.273174e+05</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>max_drawdown_pct</td>\n",
       "      <td>-2.509350e+01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>win_rate</td>\n",
       "      <td>3.947368e+01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>loss_rate</td>\n",
       "      <td>6.052632e+01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>winning_trades</td>\n",
       "      <td>7.500000e+01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>losing_trades</td>\n",
       "      <td>1.150000e+02</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>avg_pnl</td>\n",
       "      <td>-6.312911e+02</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>avg_return_pct</td>\n",
       "      <td>-5.692632e-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>avg_trade_bars</td>\n",
       "      <td>3.000000e+00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>avg_profit</td>\n",
       "      <td>2.512197e+03</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>avg_profit_pct</td>\n",
       "      <td>2.253733e+00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>20</th>\n",
       "      <td>avg_winning_trade_bars</td>\n",
       "      <td>3.000000e+00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21</th>\n",
       "      <td>avg_loss</td>\n",
       "      <td>-2.681392e+03</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>22</th>\n",
       "      <td>avg_loss_pct</td>\n",
       "      <td>-2.410348e+00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>23</th>\n",
       "      <td>avg_losing_trade_bars</td>\n",
       "      <td>3.000000e+00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>24</th>\n",
       "      <td>largest_win</td>\n",
       "      <td>1.573048e+04</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25</th>\n",
       "      <td>largest_win_pct</td>\n",
       "      <td>1.302000e+01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>26</th>\n",
       "      <td>largest_win_bars</td>\n",
       "      <td>3.000000e+00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>27</th>\n",
       "      <td>largest_loss</td>\n",
       "      <td>-1.532402e+04</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>28</th>\n",
       "      <td>largest_loss_pct</td>\n",
       "      <td>-1.262000e+01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>29</th>\n",
       "      <td>largest_loss_bars</td>\n",
       "      <td>3.000000e+00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>30</th>\n",
       "      <td>max_wins</td>\n",
       "      <td>4.000000e+00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>31</th>\n",
       "      <td>max_losses</td>\n",
       "      <td>8.000000e+00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>32</th>\n",
       "      <td>sharpe</td>\n",
       "      <td>-6.790312e-02</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>33</th>\n",
       "      <td>sortino</td>\n",
       "      <td>-6.982965e-02</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>34</th>\n",
       "      <td>profit_factor</td>\n",
       "      <td>7.611197e-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>35</th>\n",
       "      <td>ulcer_index</td>\n",
       "      <td>1.413444e+00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>36</th>\n",
       "      <td>upi</td>\n",
       "      <td>-1.941270e-02</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>37</th>\n",
       "      <td>equity_r2</td>\n",
       "      <td>8.432009e-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>38</th>\n",
       "      <td>std_error</td>\n",
       "      <td>3.923880e+04</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                      name         value\n",
       "0              trade_count  1.900000e+02\n",
       "1     initial_market_value  5.000000e+05\n",
       "2         end_market_value  3.800547e+05\n",
       "3                total_pnl -1.199453e+05\n",
       "4           unrealized_pnl -1.455192e-11\n",
       "5         total_return_pct -2.398906e+01\n",
       "6             total_profit  1.884148e+05\n",
       "7               total_loss -3.083601e+05\n",
       "8               total_fees  0.000000e+00\n",
       "9             max_drawdown -1.273174e+05\n",
       "10        max_drawdown_pct -2.509350e+01\n",
       "11                win_rate  3.947368e+01\n",
       "12               loss_rate  6.052632e+01\n",
       "13          winning_trades  7.500000e+01\n",
       "14           losing_trades  1.150000e+02\n",
       "15                 avg_pnl -6.312911e+02\n",
       "16          avg_return_pct -5.692632e-01\n",
       "17          avg_trade_bars  3.000000e+00\n",
       "18              avg_profit  2.512197e+03\n",
       "19          avg_profit_pct  2.253733e+00\n",
       "20  avg_winning_trade_bars  3.000000e+00\n",
       "21                avg_loss -2.681392e+03\n",
       "22            avg_loss_pct -2.410348e+00\n",
       "23   avg_losing_trade_bars  3.000000e+00\n",
       "24             largest_win  1.573048e+04\n",
       "25         largest_win_pct  1.302000e+01\n",
       "26        largest_win_bars  3.000000e+00\n",
       "27            largest_loss -1.532402e+04\n",
       "28        largest_loss_pct -1.262000e+01\n",
       "29       largest_loss_bars  3.000000e+00\n",
       "30                max_wins  4.000000e+00\n",
       "31              max_losses  8.000000e+00\n",
       "32                  sharpe -6.790312e-02\n",
       "33                 sortino -6.982965e-02\n",
       "34           profit_factor  7.611197e-01\n",
       "35             ulcer_index  1.413444e+00\n",
       "36                     upi -1.941270e-02\n",
       "37               equity_r2  8.432009e-01\n",
       "38               std_error  3.923880e+04"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result = strategy.backtest(calc_bootstrap=True)\n",
    "result.metrics_df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th>lower</th>\n",
       "      <th>upper</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>name</th>\n",
       "      <th>conf</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th rowspan=\"3\" valign=\"top\">Profit Factor</th>\n",
       "      <th>97.5%</th>\n",
       "      <td>0.183218</td>\n",
       "      <td>1.073593</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>95%</th>\n",
       "      <td>0.219456</td>\n",
       "      <td>0.933546</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>90%</th>\n",
       "      <td>0.263545</td>\n",
       "      <td>0.797980</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th rowspan=\"3\" valign=\"top\">Sharpe Ratio</th>\n",
       "      <th>97.5%</th>\n",
       "      <td>-0.377566</td>\n",
       "      <td>-0.010510</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>95%</th>\n",
       "      <td>-0.356633</td>\n",
       "      <td>-0.043362</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>90%</th>\n",
       "      <td>-0.330796</td>\n",
       "      <td>-0.081396</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                        lower     upper\n",
       "name          conf                     \n",
       "Profit Factor 97.5%  0.183218  1.073593\n",
       "              95%    0.219456  0.933546\n",
       "              90%    0.263545  0.797980\n",
       "Sharpe Ratio  97.5% -0.377566 -0.010510\n",
       "              95%   -0.356633 -0.043362\n",
       "              90%   -0.330796 -0.081396"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#应用 bootstrap 方法来计算两个性能指标的置信区间，即利润因子和夏普比率：\n",
    "result.bootstrap.conf_intervals"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>amount</th>\n",
       "      <th>percent</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>conf</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>99.9%</th>\n",
       "      <td>-76263.20</td>\n",
       "      <td>-15.638846</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>99%</th>\n",
       "      <td>-60642.38</td>\n",
       "      <td>-12.594680</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>95%</th>\n",
       "      <td>-47862.87</td>\n",
       "      <td>-10.097641</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>90%</th>\n",
       "      <td>-41553.27</td>\n",
       "      <td>-8.861809</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "         amount    percent\n",
       "conf                      \n",
       "99.9% -76263.20 -15.638846\n",
       "99%   -60642.38 -12.594680\n",
       "95%   -47862.87 -10.097641\n",
       "90%   -41553.27  -8.861809"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result.bootstrap.drawdown_conf"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pybroker",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
